#pragma once

#include "menu.hpp"

namespace beef {

    template <uint8_t N>
    class SpinBoxPack {
       private:
        ConstantStrArrayPtr _list = nullptr;  // 选项列表

        uint8_t _value[N] = {0};  // 当_list != nullptr 时，用_value[0] 表示当前选中的列表索引，
                                  // 否则表示每一位box 的当前值

        PointType _x = 0;
        PointType _y = 0;             // 最左侧Box 在屏幕上的位置，基准点是box 字符的坐标基准，U8G2 的设定是字符左下角
        PointType _w = 0;             // 单个Box 的宽度
        PointType _h = 0;             // 高度
        PointType _spacing = 0;       // 单个Box 之间的间隔
        PointType _right_x = 0;       // 计算得到的最右侧BOX 显示区域右边界的X 坐标，用于辅助布局
        // PointType _bar_offset_y = 0;  // 光标线相对于box 底部的垂直偏移

        uint8_t _min = 0;            // 所有box 的最小值
        uint8_t _max = 0;            // 所有box 的最大值 + 1，当_list != nullptr 时，表示list 长度
        uint8_t _left_most_max = 0;  // 当_left_most_max > _min，且_box_count > 1 时，规定左侧box 的最大值,
                                     // 目的是限制总体数字最大值为类似3999 的形式，除了左侧最高位最大值是3，其他的都是9

        uint8_t _box_index = 0;  // 当前正在编辑的box 索引，显示上，index == 0 的box 在最右边
        uint8_t _box_count = 0;  // 可用于编辑的最大box 数量，不超过N

        bool _focused = false;   // 整组SpinBoxPack 是否被聚焦
        bool _changed = false;   // 是否应该重绘
        bool _hex = false;       // 是否用16进制显示数字。如果max 小于10，则是否为true 都没影响
        bool _no_carry = false;  // 默认启用自动进位
        bool _no_cycle = false;  // 默认启用循环，允许box 当前值从min 调到max 或者反之

       public:
        SpinBoxPack() = default;


        SpinBoxPack(ConstantStrArrayPtr option_list, uint8_t option_count) :
            _list(option_list), _max(option_count), _box_count(1) {}


        SpinBoxPack(uint8_t count, uint8_t max, uint8_t min = 0, uint8_t left_most_max = 0) :
            _box_count(count), _min(min), _max(max) {}


        bool draw(bool is_initial) {
            if (!is_initial && !changed()) {
                // 没有变化，不需要重绘
                return false;
            }

            u8g2.setFont(MENU_FONT);
            auto font_height = u8g2.getAscent();
            auto top_y = _y - font_height;  // 顶部Y坐标
            // auto total_width = _w * _box_count + _spacing * (_box_count - 1);
            // _right_x = x + (_w + _spacing) * _box_count - _spacing;
            auto total_width = _right_x - _x;
            // _right_x = _x + static_cast<PointType>(total_width);
            // 清空整个区域，全部重新绘制
            // 如果用链表结构的的SpinBox，可以哪一位变了就只改哪一位，但是这一步应该没什么成本
            oled_clear_box(_x, top_y, static_cast<PointType>(total_width), _h);

            auto bottom_y = top_y + _h;
            auto box_x = _x;
            // 从左侧第一个box 开始计算坐标并绘制
            for (int i = _box_count - 1; i >= 0; --i) {
                if (_focused && i == _box_index) {
                    // 绘制选中的box 底部的光标线
                    u8g2.drawBox(box_x, bottom_y - 2, _w, 2);
                }

                u8g2.setCursor(box_x, _y);
                auto v = _value[i];

                if (_list == nullptr) {
                    // 如果没有选项列表，直接显示当前值
                    if (_hex) {
                        u8g2.print(v, HEX);
                    }
                    else {
                        u8g2.print(v);
                    }

                    box_x += _spacing + _w;
                }
                else {
                    // 如果有选项列表，显示当前选中的选项
                    if (v < _max) {
                        u8g2.print(_list[v]);
                    }
                    else {
                        u8g2.print("N/A");  // 如果索引越界，显示"N/A"
                    }
                    // _list != nullptr 时，循环只执行一次，绘制一个box
                    break;
                }
            }

            return true;
        }


        void inc(uint8_t i) {
            for (; i < _box_count; ++i) {
                auto box_max = max(i);
                if (_value[i] < box_max - 1) {
                    ++_value[i];
                    set_changed(true);
                    return;
                }
                else if (!_no_cycle) {
                    _value[i] = _min;  // 循环到最小
                    set_changed(true);

                    if (!_no_carry) {
                        continue;  // 继续循环处理下一位
                    }
                }

                // 不允许循环，或允许循环但不允许进位
                return;
            }
        }


        void inc() {
            inc(_box_index);
        }


        void dec(uint8_t i) {
            for (; i < _box_count; ++i) {
                auto box_max = max(i);
                if (_value[i] > _min) {
                    --_value[i];
                    set_changed(true);
                }
                else if (!_no_cycle) {
                    _value[i] = box_max - 1;  // 循环到max - 1
                    set_changed(true);

                    if (!_no_carry) {
                        continue;  // 继续循环处理下一位
                    }
                }

                // 不允许循环，或允许循环但不允许借位
                return;
            }
        }


        void dec() {
            dec(_box_index);
        }


        bool changed() const {
            return _changed;
        }


        void set_changed(bool c) {
            _changed = c;
        }


        uint8_t box_count() const {
            return _box_count;
        }


        uint8_t focused_index() const {
            return _box_index;
        }


        void set_focused_index(uint8_t index) {
            if (index < _box_count) {
                _box_index = index;
                _focused = true;
                _changed = true;
            }
        }


        void set_focused(bool f) {
            _focused = f;
            _changed = true;
        }


        bool focused() const {
            return _focused;
        }


        bool hex() const {
            return _hex;
        }


        void set_hex(bool b) {
            _hex = b;
        }


        bool carry() const {
            return !_no_carry;
        }


        void set_carry(bool b) {
            _no_carry = !b;
        }


        bool cycle() const {
            return !_no_cycle;
        }


        void set_cycle(bool b) {
            _no_cycle = !b;
        }


        void set_range(uint8_t count, uint8_t min, uint8_t max) {
            _box_count = count > N ? N : count;
            _min = min;
            _max = max;
        }


        void set_range(uint8_t min, uint8_t max) {
            _min = min;
            _max = max;
        }


        void set_left_most_max(uint8_t m) {
            _left_most_max = m;
        }


        void reset_box_value(uint8_t value) {
            value = constrain(value, _min, _max - 1);
            for (uint8_t i = 0; i < _box_count; ++i) {
                _value[i] = value;
            }
            if (_left_most_max > 0 && _box_count > 1) {
                _value[_box_count - 1] = constrain(_value[_box_count - 1], _min, _left_most_max - 1);
            }
            set_changed(true);
        }


        uint8_t min() const {
            return _min;
        }


        uint8_t max() const {
            return _max;
        }


        uint8_t left_most_max() const {
            return _left_most_max;
        }


        uint8_t max(uint8_t index) const {
            if (_box_count > 1 && index == _box_count - 1) {
                return _left_most_max > 0 ? _left_most_max : _max;
            }
            return _max;
        }


        void set_list(ConstantStrArrayPtr list, uint8_t count, uint8_t current = 0) {
            _list = list;
            _max = count;
            _min = 0;
            _box_count = 1;                             // 有选项列表时，只能有一个box
            _value[0] = current < count ? current : 0;  // 确保当前索引不越界
        }


        uint8_t current() const {
            return _value[_box_index];
        }


        const char* current_text() const {
            if (_list != nullptr) {
                auto v = _value[0];
                if (v < _max) {
                    return _list[v];
                }
            }
            return nullptr;  // 如果没有选项列表或索引越界，返回nullptr
        }


        uint8_t value(uint8_t index) const {
            if (index < _box_count) {
                return _value[index];
            }
            return 0;
        }


        uint8_t set_value(uint8_t index, uint8_t v) {
            if (index < _box_count) {
                v = constrain(v, _min, max(index) - 1);
                _value[index] = v;
                set_changed(true);
                return v;
            }
            return 0;
        }


        void set_box_index(uint8_t index) {
            if (index < _box_count) {
                _box_index = index;
            }
        }


        /**
         * @brief
         *
         * @return true   如果已经是最左侧的Box，则回到最右侧
         * @return false
         */
        bool focus_left() {
            if (_box_count == 0)
                return false;

            bool flag = false;
            if (_box_index < _box_count - 1) {
                ++_box_index;  // 聚焦左一个Box
            }
            else {
                _box_index = 0;  // 如果已经是最左侧的Box，则回到最右侧
                flag = true;
            }

            _changed = true;
            return flag;
        }


        bool focus_right() {
            if (_box_count == 0)
                return false;

            bool flag = false;
            if (_box_index > 0) {
                --_box_index;  // 聚焦右一个Box
            }
            else {
                _box_index = _box_count - 1;  // 如果已经是最右侧的Box，则回到最左侧
                flag = true;
            }

            _changed = true;
            return flag;
        }


        void set_placing(PointType x, PointType y, PointType w, PointType h, PointType spacing = 0) {
            _x = x;
            _y = y;
            _w = w;
            _h = h;
            _spacing = spacing;
            _right_x = x + (_w + _spacing) * _box_count - _spacing;
            _changed = true;
        }


        PointType x() const {
            return _x;
        }


        PointType y() const {
            return _y;
        }


        PointType width() const {
            return _w;
        }


        PointType height() const {
            return _h;
        }


        PointType spacing() const {
            return _spacing;
        }


        PointType right_x() const {
            return _right_x;
        }


        uint32_t sum(uint8_t base = 10) const {
            uint32_t sum = 0;
            uint32_t multiplier = 1;

            for (uint8_t i = 0; i < _box_count; ++i) {
                auto b = (base > 0) ? base : max(i);  // 如果没有基数，则使用最大值
                sum += _value[i] * multiplier;
                multiplier *= b;
            }

            return sum;
        }


        void split_sum(uint32_t sum, uint8_t base = 10) {
            for (uint8_t i = 0; i < _box_count; ++i) {
                auto b = (base > 0) ? base : max(i);     // 如果没有基数，则使用最大值
                auto r = static_cast<uint8_t>(sum % b);  // 计算余数
                sum /= b;
                r = constrain(r, _min, max(i) - 1);  // 确保值在范围内
                _value[i] = r;                       // 设置当前box 的值
            }

            set_changed(true);  // 设置为已改变状态
        }
    };
}  // namespace beef